iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
Modern Web

Fastify 101系列 第 29

[Fastify] Day29 - Deployment

  • 分享至 

  • xImage
  •  

大家好,我是 Yubin

今天要介紹 Fastify App 部屬的相關實踐及注意事項。


NodeJS 的標準函式庫中有內建著 Web Server 的框架 (http module)。

不像 PHP 或 Python 等語言需要特定支援該語言的 Web Server 或設定 CGI Gateway

使用 NodeJS 可以直接寫處理 HTTP Request 的程式,可以很容易的在程式裡面處理多個 domain 來的 Request 或聽多個 Port (http/https)。

然而,Fastify 官方團隊 強烈不建議 這樣的作法。

因為會增加不必要的複雜性,也會讓應用程式的功能失焦,更可能會妨礙到水平擴展的進行。

所以使用 Reverse Proxy 來搭配部屬的工作。

Reverse Proxy

不清楚反向代理 (Reverse Proxy) 的朋友可以參考這篇
或 Nginx 官方網站的文件: What Is a Reverse Proxy Server?

這篇文章也闡述了為什麼 NodeJS 伺服器應該搭配反向代理。

使用 Reverse Proxy,特別是當你有下列的需求:

  • 你的 App 需要多個 instance 來處理附載
  • TSL Termination
  • App 需要把 HTTP redirect 到 HTTPS
  • App 有多個 domain 的需求
  • App 需要 Serve 靜態檔案

要架設 Reverse Proxy 有多種選擇,這邊使用 nginx 來操作。

Nginx

nginx 的安裝可以參考官方文件

編輯 nginx config:

upstream fastify_app {
  server 10.0.0.1:80;
  server 10.0.0.2:80;
  server 10.0.0.3:80;
}

server {
  listen 80;
  listen [::]:80;
  server_name yubin.tw;

  location / {
    return 301 https://$host$request_uri;
  }
}

server {
  listen 443 ssl http2 default_server;
  listen [::]:443 ssl http2 default_server;

  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name yubin.tw;

  ssl_certificate /path/to/fullchain.pem;
  ssl_certificate_key /path/to/private.pem;
}

location / {
    # more info: https://nginx.org/en/docs/http/ngx_http_proxy_module.html
    proxy_http_version 1.1;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_pass http://fastify_app;
}

上述 config 可以參考以下架構閱讀。

https://ithelp.ithome.com.tw/upload/images/20221014/20151148CC5F4ivwJO.jpg

Nginx 作為 Proxy Server,把來自 http 且 Request 的目標 host 是 yubin.tw 的都 redirect 到 https。迫使 client 端使用 https 來與我們進行互動。

https 用的 SSL 證書也是定義在這邊。

然後把所有 request 都送進我們定義的 fastify_app,也就是內部三台 Fastify Server,IP 位址是 10.0.0.1, 10.0.0.2, 10.0.0.3。

預設的分流模式為 Round Robin。如果想要更改預設的分流模式,可以參考這篇


Config

根據 The Twelve Factors 的描述,我們應該把我們的設定以環境變數的方式提供給程式做讀取。

Store config in the environment

process.env

在 NodeJS 中,要讀取環境變數可以透過 process.env

const port = process.env.FASTIFY_PORT

但是每個 config 都從環境變數讀,要設定或更改環境變數可能很麻煩。

這邊可以使用 dotenv 這個工具來協助我們進行開發。

dotenv

dotenv 是 NodeJS 的 Module,透過 npm 就可以安裝:

npm install -D dotenv

接著我們新增一個叫做 .env 的檔案:

FASTIFY_APP=8888

.env 的格式為一行一筆環境變數,由等號(=)分隔,前面是環境變數的名稱,後面是

定義好後,在程式中使用 dotenv 的 .config() 方法:

import * as dotenv from 'dotenv'

dotenv.config()

const port = process.env.FASTIFY_PORT

dotenv.config() 這個方法會去找你有沒有 .env 或相關檔案,有的話會把 .env 中讀到的內容,放進 process.env 中,所以程式內就可以透過 process.env.XXX 的方式拿到環境變數的值。

要注意的是,dotenv 並不會真的去修改環境變數,他只是把 .env 描述的東西,放進 process.env 這個物件裡面。

更重要的是,每個利用 process.env 讀到的環境變數,他的型態都是 string | undefined
使用上別忘了進行環境變數的型態轉換以及資料驗證


Monitoring

程式部屬上去之後,就會有維運、監控的需求。

Metric

Grafana + Prometheus + Alertmanager,是常見的 monitor stack 之一。

使用 Prometheus 蒐集 metrics,可以安裝 fastify-metrics 這個 Plugin。

安裝 fastify-metrics

npm i fastify-metrics

透過 server.register() 進行註冊,並設定 endpoint 為 /metrics (Prometheus scrape job 預設 path)。

import fastifyMetric from 'fastify-metrics'

server.register(fastifyMetric, { endpoint: '/metrics' })

這個 plugin 會幫我們把 App 的 CPU、Memory 等資訊轉換為 Prometheus 的資料格式。

https://ithelp.ithome.com.tw/upload/images/20221014/20151148i0xBnRfXU2.jpg

Log

Log 常見的是使用 Elasticsearch + Fluentd + Kibana,合稱 EFK。

Logging 的動作,不要使用 console.log 來進行,而是使用內建的 Pino

可以參考 Fastify101: Logging

Trace

Trace 常見的是使用 Open Telemetry 這個 Trace 的標準。

Open Telemetry 也對 NodeJS 有良好的支援,可以參考官網的教學
或這個 GitHub Repo 的完整範例。


Health Check

程式執行的過程中,要如何知道現在的健康狀態。

絕對不是程式還在執行就是健康,可能 Database 的連線已經斷了,也可能某個重要的外部服務已經存取不到。

我們可以準備一個 Endpoint,來檢查目前應用程式的狀態。

這邊可以使用 fastify-custom-healthcheck 這個 plugin。

使用 npm 進行安裝。

npm i fastify-custom-healthcheck

接著透過 server.register() 進行註冊,並定義 Endpoint。

import customHealthCheck from 'fastify-custom-healthcheck'

server.register(customHealthCheck, {
    path: '/health'
})

就可以多一個 Health Check 用的 Endpoint。

https://ithelp.ithome.com.tw/upload/images/20221014/201511483GuLr1vsOT.jpg

當你需要在 Health Check 的時候一並執行其他檢查項目,可以利用 .addHealthCheck 來操作。

server.register(customHealthCheck, {
  path: '/health'
}).after(() => {
  server.addHealthCheck('mongo', async () => {
    return checkMongoConnection()  // return Promise
  })
})

在 /health 上就會多一個欄位:

https://ithelp.ithome.com.tw/upload/images/20221014/20151148AbTC6ENefK.jpg

如果有多個項目需要做檢查,只要透過 .addHealthCheck 新增檢查的 function 就可以了。

如果有一個檢查沒有通過,Endpoint 就會回傳 500 的狀態碼,所有檢查都通過則回傳 200


Kubernetes Probe

在現在這個 Cloud Native 當道的年代,應用程式部屬到 Kubernetes 是非常常見的。

我們在撰寫 deployment.yaml 的時候,有沒有良好的定義 Probe 也是非常重要的。

Kubernetes 有三個 Probe:

  • Liveness Probe 告訴 Kubernetes 什麼時候要重啟 Container。
  • Readiness Probe 告訴 Kubernetes 能不能把流量送進該 Container。
  • Startup Probe 告訴 Kubernetes 這個 Container 是不是已經啟動完成。

Probe 的操作,可以使用 cmd 或 http 或 tcp 或 gRPC 的方式來戳。

  • 使用 command 來執行 livenessProbe:
livenessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5
  • 使用 http request 來執行 readinessProbe:
readinessProbe:
  httpGet:
    path: /health
    port: 8888
  initialDelaySeconds: 5
  periodSeconds: 5

使用 tcp、gRPC 可以參考官方文件

良好設計這些 Probe,告訴 Kubernetes 現在應用程式的狀態是非常重要的。


本文介紹了一些部屬相關的東西,由於我們的程式都會以 Container 的形式存在,所以怎樣把 image 包的好包的洽當也是很重要的。如果不清楚的朋友可以參考 Fastify101: Containerization

開發是一回事,部屬之後又是另一個世界。也是你寫的東西真正開始產生價值的時候,千萬不得馬虎。


上一篇
[Fastify] Day28 - Containerization (Dockerfile)
下一篇
[Fastify] Day30 - Graceful Shutdown
系列文
Fastify 10130
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言